package top.csmcool.musicapi.service.impl;

/*
 * @description
 * @date 2025/1/27
 * @author shijin
 * @version 1.0
 */

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import top.csmcool.musicapi.client.HaloClient;
import top.csmcool.musicapi.client.NavidromeClient;
import top.csmcool.musicapi.config.SystemProperties;
import top.csmcool.musicapi.domain.UserPlatformBindInfo;
import top.csmcool.musicapi.domain.Users;
import top.csmcool.musicapi.enums.PlatformEnum;
import top.csmcool.musicapi.enums.StatusCode;
import top.csmcool.musicapi.model.api.navidrome.NavidromeLoginResult;
import top.csmcool.musicapi.model.api.navidrome.NavidromeUserInfo;
import top.csmcool.musicapi.model.result.Request;
import top.csmcool.musicapi.model.result.Result;
import top.csmcool.musicapi.model.system.PlatformInfo;
import top.csmcool.musicapi.model.user.UserLogin;
import top.csmcool.musicapi.service.UserPlatformBindInfoService;
import top.csmcool.musicapi.service.UserService;
import top.csmcool.musicapi.service.UsersService;
import top.csmcool.musicapi.util.GlobalCache;
import top.csmcool.musicapi.util.ResultUtil;

import java.util.*;

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final UsersService usersService;
    private final UserPlatformBindInfoService userPlatformBindInfoService;
    private final SystemProperties systemProperties;
    private final NavidromeClient navidromeClient;
    private final HaloClient haloClient;

    @Value("${sa-token.timeout:2592000}")
    private long tokenTime;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<SaTokenInfo> login(@RequestBody Request<UserLogin> requestRequest) {
        UserLogin body = requestRequest.getBody();
        if(!StringUtils.hasText(body.getUsername()) || !StringUtils.hasText(body.getPassword())) {
            return ResultUtil.failure(StatusCode.NEED_PARAM);
        }

        Users userInfo = haloClient.getUserInfo(body.getUsername(), body.getPassword());

        if(userInfo == null) {
            return ResultUtil.failure(StatusCode.LOGIN_FAILED);
        }

        if(userInfo.getDisabled()) {
            return ResultUtil.failure(StatusCode.ACCOUNT_DISABLED);
        }

        if(!userInfo.getEmailVerified()) {
            return ResultUtil.failure(StatusCode.EMAIL_NOT_VERIFIED);
        }

        Users users = saveUserInfo(userInfo, null, null, body.getUsername(), body.getPassword());
        boolean saveUserInfo = users != null;

        if(!saveUserInfo) {
            return ResultUtil.failure(StatusCode.USER_INFO_SAVE_FAILED);
        }

        // 第1步，先登录上
        StpUtil.login(users.getId());
        // 第2步，获取 Token  相关参数
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();

        return ResultUtil.success(tokenInfo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<SaTokenInfo> adminLogin(Request<UserLogin> requestRequest) {
        // 管理员登录流程
        UserLogin body = requestRequest.getBody();
        String username = systemProperties.getUsername();
        String password = systemProperties.getPassword();

        if(!username.equals(body.getUsername()) ||
                !password.equals(body.getPassword())) {
            return ResultUtil.failure(StatusCode.LOGIN_FAILED);
        }

        Users adminUser = new Users();
        adminUser.setId(1L);
        adminUser.setEmail("admin@admin.com");
        adminUser.setPassword("");
        adminUser.setDisplayName(username);
        adminUser.setUsername(username);
        adminUser.setEmailVerified(true);
        adminUser.setDisabled(false);
        adminUser.setTwoFactorAuthEnabled(false);
        adminUser.setRegisteredAt(new Date());
        Users users = saveUserInfo(null, 1, adminUser, body.getUsername(), body.getPassword());
        if(users == null) {
            return ResultUtil.failure(StatusCode.USER_INFO_SAVE_FAILED);
        }
        // 0号玩家管理员
        StpUtil.login(1L);
        // 第2步，获取 Token  相关参数
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();

        return ResultUtil.success(tokenInfo);
    }

    @Override
    public Result<Users> getUserInfo() {
        QueryWrapper<Users> queryWrapper = new QueryWrapper<>();
        if (!StpUtil.isLogin()) {
            return ResultUtil.failure(StatusCode.NEED_LOGIN);
        }
        queryWrapper.eq("id", StpUtil.getLoginId());
        Users userMusicPlatform = usersService.getOne(queryWrapper);
        return ResultUtil.success(userMusicPlatform);
    }

    @Override
    public Result<List<PlatformInfo>> getUserPlatform() {
        List<PlatformInfo> result = new ArrayList<>();
        if (!StpUtil.isLogin()) {
            return ResultUtil.failure(StatusCode.NEED_LOGIN);
        }

        QueryWrapper<UserPlatformBindInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", StpUtil.getLoginId());
        List<UserPlatformBindInfo> list = userPlatformBindInfoService.list(queryWrapper);
        for (UserPlatformBindInfo userPlatformBindInfo : list) {
            PlatformInfo platformInfo = new PlatformInfo();

            String platformCode = userPlatformBindInfo.getPlatformCode();
            String platformName = userPlatformBindInfo.getPlatformName();
            platformInfo.setPlatformName(platformName);
            platformInfo.setPlatformCode(platformCode);
            platformInfo.setDisable(userPlatformBindInfo.getIsDisabled() == 1);

            result.add(platformInfo);
        }

        return ResultUtil.success(result);
    }

    @Override
    public Result<List<UserPlatformBindInfo>> getPlatformInfos() {
        if (!StpUtil.isLogin()) {
            return ResultUtil.failure(StatusCode.NEED_LOGIN);
        }
        QueryWrapper<UserPlatformBindInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", StpUtil.getLoginId());
        Page<UserPlatformBindInfo> page = new Page<>();
        Page<UserPlatformBindInfo> page1 = userPlatformBindInfoService.page(page);
        List<UserPlatformBindInfo> list = userPlatformBindInfoService.list(queryWrapper);
        return ResultUtil.success(list);
    }

    @Override
    public Result<Users> updateUserInfo(Request<Users> request) {
        if (!StpUtil.isLogin()) {
            return ResultUtil.failure(StatusCode.NEED_LOGIN);
        }
        Users entity = new Users();
        entity.setId((Long) StpUtil.getLoginId());
        entity.setAvatar(request.getBody().getAvatar());
        entity.setDisplayName(request.getBody().getDisplayName());
        boolean b = usersService.updateById(entity);
        if(!b) {
            return ResultUtil.failure(StatusCode.USER_INFO_UPDATE_FAILED);
        }
        return ResultUtil.success(entity);
    }

    @Override
    public Result<UserPlatformBindInfo> updatePlatformInfo(Request<UserPlatformBindInfo> request) {
        if (!StpUtil.isLogin()) {
            return ResultUtil.failure(StatusCode.NEED_LOGIN);
        }
        String platformUrl = request.getBody().getPlatformUrl();
        UrlBuilder builder = UrlBuilder.ofHttp(platformUrl, CharsetUtil.CHARSET_UTF_8);
        String host = builder.getHost();
        String ipByHost = NetUtil.getIpByHost(host);
        boolean innerIP = NetUtil.isInnerIP(ipByHost);
        // 内网ip不允许用户自己设置
        if(innerIP) {
            return ResultUtil.failure(StatusCode.USER_INFO_UPDATE_FAILED);
        }
        QueryWrapper<UserPlatformBindInfo> updateWrapper = new QueryWrapper<>();
        updateWrapper.eq("id", request.getBody().getId());
        updateWrapper.eq("user_id", StpUtil.getLoginId());
        boolean b = userPlatformBindInfoService.update(request.getBody(), updateWrapper);
        if(!b) {
            return ResultUtil.failure(StatusCode.USER_INFO_UPDATE_FAILED);
        }
        return ResultUtil.success(request.getBody());
    }

    public Users saveUserInfo(Users spec, Integer id, Users adminUser, String username, String password) {
        boolean saveResult = true;
        // 不存在保存信息
        QueryWrapper<Users> queryWrapper = new QueryWrapper<>();
        if(id == null) {
            queryWrapper.eq("email", spec.getEmail());
        } else {
            queryWrapper.eq("id", id);
        }
        Users userMusicPlatform = usersService.getOne(queryWrapper);
        Users users = null;
        if(userMusicPlatform == null) {
            if(adminUser == null) {
                users = spec;
            } else {
                users = adminUser;
            }
            users.setUsername(username);
            if(users == null) {
                return null;
            }
            saveResult = usersService.save(users);
            userMusicPlatform = users;
        } else if (adminUser != null &&
                !username.equals(userMusicPlatform.getUsername())) {
            QueryWrapper<Users> updateWrapper = new QueryWrapper<>();
            updateWrapper.eq("id", 1);
            usersService.update(adminUser, updateWrapper);
        }
        // 避免用户先注册的情况 重新保存保证id为 1的不要被占用
        if(userMusicPlatform.getId() == 1 && adminUser == null) {
            // 删除重新来过
            usersService.removeById(1);
            saveResult = usersService.save(users);
        }
        if(saveResult) {
            // 先查询在绑定
            QueryWrapper<UserPlatformBindInfo> queryWrapper1 = new QueryWrapper<>();
            queryWrapper1.eq("user_id", userMusicPlatform.getId());
            List<UserPlatformBindInfo> list = userPlatformBindInfoService.list(queryWrapper1);
            List<String> codeList = list.stream().map(UserPlatformBindInfo::getPlatformCode).toList();

            // 平台绑定
            userMusicPlatform.setPassword(password);
            ArrayList<UserPlatformBindInfo> entityList = getUserPlatformBindInfos(codeList, userMusicPlatform);

            if(!entityList.isEmpty()) {
                saveResult = userPlatformBindInfoService.saveBatch(entityList);
            }
        }

        if(!saveResult) {
            throw new RuntimeException("保存平台信息失败");
        }

        userMusicPlatform.setPassword(password);
        GlobalCache.put(String.valueOf(userMusicPlatform.getId()),
                userMusicPlatform, tokenTime * 1000);

        return userMusicPlatform;
    }

    private ArrayList<UserPlatformBindInfo> getUserPlatformBindInfos(List<String> codeList, Users userMusicPlatform) {
        PlatformEnum[] values = PlatformEnum.values();
        ArrayList<UserPlatformBindInfo> entityList = new ArrayList<>();

        for (PlatformEnum value : values) {
            if(codeList.contains(value.getCode())) {
                continue;
            }
            UserPlatformBindInfo userPlatformBindInfo = new UserPlatformBindInfo();
            userPlatformBindInfo.setUserId(userMusicPlatform.getId());
            userPlatformBindInfo.setPlatformName(value.getName());
            userPlatformBindInfo.setPlatformCode(value.getCode());

            if(value.getCode().equals(PlatformEnum.NAVIDROME.getCode())) {
                // 在NAVIDROME生成账号
                userPlatformBindInfo.setPlatformUrl(systemProperties.getRegisterUrl());
                userPlatformBindInfo.setUsername(userMusicPlatform.getUsername());
                // 目前不保存密码

                // 失败了不阻塞
                registerAccount(userPlatformBindInfo, userMusicPlatform);
            }

            entityList.add(userPlatformBindInfo);
        }
        return entityList;
    }

    private Users buildUsers(JSONObject spec) {
        Users bean = null;
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.configure(DeserializationFeature.ACCEPT_FLOAT_AS_INT, true);  // 如果需要将布尔类型转换为整数，可以用此配置
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        try {
            bean = objectMapper.readValue(JSONUtil.toJsonStr(spec), Users.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return bean;
        }

        return bean;
    }

    private String encode(byte[] content) {
        byte[] encode = Base64.getEncoder().encode(content);
        return new String(encode);
    }

    private void registerAccount(UserPlatformBindInfo userPlatformBindInfo, Users userMusicPlatform) {
        try {
            NavidromeLoginResult tokenInfo = navidromeClient.getTokenInfo();
            String token = tokenInfo.getToken();
            String id = tokenInfo.getId();

            NavidromeUserInfo userInfos = navidromeClient.getUserInfos(userMusicPlatform.getUsername(), token, id);
            if(userInfos != null) {
                userPlatformBindInfo.setPlatformId(userInfos.getId());
                return;
            }

            String s = navidromeClient.adUserInfos(userMusicPlatform, token, id);
            userPlatformBindInfo.setPlatformId(s);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
